using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using WWB.DisqusSharp.Model.DisqusDTO;

namespace WWB.DisqusSharp.Model.DisqusService
{
    public class DisqusServiceErrorWrapper<T> : DisqusServiceResultWrapper<string>, IDisqusServiceResult<T>
    {
        public DisqusServiceErrorWrapper(string json) : base(json)
        {
        }

        #region Implementation of IDisqusServiceResult<T>

        T IDisqusServiceResult<T>.Payload
        {
            get { return default(T); }
        }

        #endregion
    }

    public class DisqusServiceResultWrapper<T> : DisqusServiceResultWrapper, IDisqusServiceResult<T> where T : class
    {
        public DisqusServiceResultWrapper(string json) : base(json)
        {
            Payload = GetPayLoadAs<T>();
        }

        #region Implementation of IDisqusServiceResult<T>

        public T Payload { get; private set; }

        #endregion
    }

    public class DisqusServiceResultWrapper : DisqusServiceResultBase
    {
        public DisqusServiceResultWrapper(string json)
        {
            if (string.IsNullOrEmpty(json))
            {
                throw new ArgumentNullException("json", "json cannot be null or empty");
            }
            var strRes =  JsonConvert.DeserializeObject<DisqusServiceResultBase>(json);
            if (strRes != null)
            {
                Code = strRes.Code;
                Succeeded = strRes.Succeeded;
            }
            this.json = json;
        }

        private readonly string json;
        private JObject jsonQueryable;

        protected override string ErrorMessage
        {
            get { return IsError ? GetPayLoadAs<string>() : string.Empty; }
        }

        /// <summary>
        /// Gets the payload object in a strongly typed manner.
        /// </summary>
        /// <typeparam name="T">Return type</typeparam>
        /// <returns>Payload deserialzed as <typeparamref name="T"/> or null if not possible.</returns>
        public T GetPayLoadAs<T>() where T : class
        {
            if (jsonQueryable == null)
            {
                jsonQueryable = JObject.Parse(json);
            }
            var node = jsonQueryable.SelectToken("message");
            if (node == null)
            {
                return default(T);
            }
            var targetType = typeof (T);
            if (targetType == typeof(string))
            {
                if (node.Type == JTokenType.String)
                {
                    return node.Value<string>() as T;       
                }
                return node.ToString() as T;
            }
            if (targetType == typeof(ForumPostCount))
            {
                return ForumPostCount.FromJson(node) as T;
            }
            JsonSerializer jsz = new JsonSerializer();
            try
            {
                return jsz.Deserialize<T>(new JTokenReader(node));
            }
            catch (JsonSerializationException jsex)
            {
                throw new DisqusServiceSerializationException<T>(json, "Deserialization failed for type " + typeof(T), jsex);
            }
        }

        public override string JsonPayload
        {
            get
            {
                return ToString();
            }
        }

        /// <summary>
        /// Gets original json string.
        /// </summary>
        /// <returns>Object as json string.</returns>
        public override string ToString()
        {
            return json;
        }
    }
}